#include <string_view>
#include <immintrin.h>
#include <iostream>
#include "UnicodeConverter.hpp"
Level5::UnicodeConverter::UnicodeConvertException::UnicodeConvertException() :
	Exception(U"Unicode Convert Failed!")
{
}
std::u32string Level5::UnicodeConverter::UnicodeConvertException::What()const
{
	return _what;
}
unsigned BitCount(char8_t ch)
{
	char v(ch);
	unsigned long index;
	_BitScanReverse(&index, ~v);
	return 7 - index;
}
std::u32string Level5::UnicodeConverter::ConvertFromUTF8ToUTF32(const std::u8string& _val)
{
	if (_val.empty())
		return U"";
	std::u32string str;
	std::u8string val(_val);
	char8_t* it(&val.at(0));
	char32_t ch;
	unsigned long index;
	while (*it)
	{
		if (*it & 0x80)
		{
			index = BitCount(*it);
			ch = *it & (0xFF >> (index + 1));
			for (unsigned i(1); i < index; ++i)
			{
				if (!*it)
					throw Level5::UnicodeConverter::UnicodeConvertException();
				++it;
				if ((*it >> 6) == 2)
					ch = (ch << 6) | (*it & 0x3F);
				else
					throw Level5::UnicodeConverter::UnicodeConvertException();
			}
		}
		else
			ch = *it;
		++it;
		str.push_back(ch);
	}
	return str;
}
std::u32string Level5::UnicodeConverter::ConvertFromUTF16ToUTF32(const std::u16string& _val)
{
	if (_val.empty())
		return U"";
	std::u32string str;
	std::u16string val(_val);
	char16_t* it(&val.at(0));
	char32_t ch;
	while (*it)
	{
		if (*it >= 0xD800 && *it <= 0xDBFF)
		{
			ch = (*it & 0x03FF) << 10;
			++it;
			if (*it >= 0xDC00 && *it <= 0xDFFF)
				ch |= *it & 0x03FF;
			else
				throw Level5::UnicodeConverter::UnicodeConvertException();
		}
		else
			ch = *it;
		++it;
		str.push_back(ch);
	}
	return str;
}
std::u8string Level5::UnicodeConverter::ConvertFromUTF32ToUTF8(const std::u32string& _val)
{
	if (_val.empty())
		return u8"";
	char8_t buf[8];
	std::u8string str;
	std::u32string val(_val);
	char32_t* it(&val.at(0));
	char8_t ch;
	int count;
	unsigned long index;
	while (*it)
	{
		count = 0;
		if (*it >> 7)
		{
			_BitScanReverse(&index, *it);
			while (index > 6)
			{
				buf[count++] = 0x80 | (*it & 0x3F);
				*it >>= 6;
				index -= 6;
			}
			if (index > 6 - count)
			{
				buf[count++] = 0x80 | *it;
				*it = 0;
			}
			buf[count] = (0xFF80 >> count) | *it;
		}
		else
			buf[0] = *it;
		while (count >= 0)
			str.push_back(buf[count--]);
		++it;
	}
	return str;
}
std::u16string Level5::UnicodeConverter::ConvertFromUTF32ToUTF16(const std::u32string& _val)
{
	if (_val.empty())
		return u"";
	std::u16string str;
	std::u32string val(_val);
	char32_t* it(&val.at(0));
	while (*it)
	{
		if ((*it >= 0xD800 && *it <= 0xDFFF) || *it >= 0x110000)
			throw Level5::UnicodeConverter::UnicodeConvertException();
		if (*it >= 0x100000)
		{
			str.push_back(*it >> 10);
			str.push_back(*it & 0x03FF);
		}
		else
			str.push_back(*it);
		++it;
	}
	return str;

}
std::u8string Level5::UnicodeConverter::ConvertFromUTF16ToUTF8(const std::u16string& _val)
{
	return Level5::UnicodeConverter::ConvertFromUTF32ToUTF8(
		Level5::UnicodeConverter::ConvertFromUTF16ToUTF32(
			_val
		)
	);
}
std::u16string Level5::UnicodeConverter::ConvertFromUTF8ToUTF16(const std::u8string& _val)
{
	return Level5::UnicodeConverter::ConvertFromUTF32ToUTF16(
		Level5::UnicodeConverter::ConvertFromUTF8ToUTF32(
			_val
		)
	);
}